package com.sweet.auth;

import com.sweet.enums.Charset;
import com.sweet.json.JsonUtil;
import com.sweet.lang.StringUtil;
import com.sweet.util.WebUtil;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

/**
 * ip白名单验证
 *
 * @author limi
 * @create 2018-03-23-15:49
 */
@Slf4j
public final class IPAuth {
    /**
     * 认证失败状态码
     */
    private static final int AUTH_FAILURE_CODE = 403;

    /**
     * 内网IP列表
     */
    private static final Set<String> INTRANET_IPS = new HashSet<>();

    static {
        INTRANET_IPS.add("127.");
        INTRANET_IPS.add("192.168.");
        INTRANET_IPS.add("10.");
        INTRANET_IPS.add("172.");
        INTRANET_IPS.add("0:0:0:0:0:0:0:1");        //本机IPV6地址
    }

    private IPAuth() {
    }

    /**
     * 是否包含指定ip
     *
     * @param request  本次请求
     * @param ipWhites 允许的ip白名单
     * @return
     */
    public static boolean auth(HttpServletRequest request, Set<String> ipWhites) {
        //获取请求IP
        String remoteIP = WebUtil.getRealIp(request);
        for (String prefix : ipWhites) {
            if (remoteIP.startsWith(prefix)) {
                log.debug("来访IP({}),允许访问!", remoteIP);
                return true;
            }
        }

        log.debug("来访IP({})被拦截，拒绝访问!", remoteIP);
        return false;
    }

    /**
     * 返回认证失败信息
     *
     * @param request  请求
     * @param response 响应
     * @param ip       来访ip
     * @param message  反馈信息
     * @param printLog 是否打印日志
     */
    public static void fail(HttpServletRequest request, HttpServletResponse response, String ip, Object message, boolean printLog) throws IOException {
        //输出警告日志
        if (printLog) {
            log.warn("来访IP({})被拦截!", ip);
        }

        //输出认证响应信息
        WebUtil.write(getFailResponse(response), JsonUtil.toJson(message), Charset.UTF8);
    }

    /**
     * 设置认证响应信息
     *
     * @param response
     * @return
     */
    private static HttpServletResponse getFailResponse(HttpServletResponse response) {
        //设置状态码
        response.setStatus(AUTH_FAILURE_CODE);
        return response;
    }

    /**
     * 检查ip是否为内网ip
     *
     * @param checkIP 待检查的ip地址
     * @return
     */
    public static boolean isIntranetIP(String checkIP) {
        if (StringUtil.isBlank(checkIP)) {
            return false;
        }

        for (String prefix : INTRANET_IPS) {
            if (checkIP.startsWith(prefix)) {
                return true;
            }
        }

        return false;
    }
}